
#include "renderer.h"

Interpolant::Interpolant(float _x,float _z,float _invz,float _r,float _g,float _b,float _u,float _v,float _invu,float _invv)
{
    x=_x; r=_r; g=_g; b=_b; u=_u; v=_v;
    z=_z;
    invz=_invz; invu=_invu; invv=_invv;
}

void Interpolant::set(float _x,float _z,float _invz,float _r,float _g,float _b,float _u,float _v,float _invu,float _invv)
{
    x=_x; r=_r; g=_g; b=_b; u=_u; v=_v;
    z=_z;
    invz=_invz; invu=_invu; invv=_invv;
}

void Interpolant::operator +=(Interpolant& i)
{
    x+=i.x; z+=i.z;
    r+=i.r; g+=i.g; b+=i.b;
    u+=i.u; v+=i.v;
    invz+=i.invz; invu+=i.invu; invv+=i.invv;

}
void Interpolant::operator -=(Interpolant& i)
{
    x-=i.x; z-=i.z;
    r-=i.r; g-=i.g; b-=i.b;
    u-=i.u; v-=i.v;
    invz-=i.invz; invu-=i.invu; invv-=i.invv;

}
void Interpolant::operator /=(float f)
{
    x/=f; z/=f;
    r/=f; g/=f; b/=f;
    u/=f; v/=f;
    invz/=f; invv/=f; invu/=f;
}


Renderer::Renderer()
{
    m_nLights=0;
}


void Renderer::setTexture(QImage* texture)
{
   m_texture = texture;
   m_tex=m_texture->bits();
   m_texw=m_texture->width();
   m_texh=m_texture->height();
}



void Renderer::render( RendererVertex v1, RendererVertex v2, RendererVertex v3)
{
   RendererVertex v[3];
   v[0] = v1;
   v[1] = v2;
   v[2] = v3;
   
   render( v );
}

void Renderer::transform(std::vector<Vertex3D>& vertex, RendererVertex* out)
{
    int vertexCount=vertex.size();
    gtypes::Matrix4 mat = m_modelviewMatrix * m_projectionMatrix;
    gtypes::Matrix3 nmat=gtypes::Matrix3(m_modelviewMatrix);
    for( int i = 0; i < vertexCount; i++ )
    {
        out[i].pos = m_modelviewMatrix * vertex[i].pos;
        out[i].pos= m_projectionMatrix * out[i].pos;
        out[i].pos.x=out[i].pos.x/out[i].pos.z;
        out[i].pos.y=out[i].pos.y/out[i].pos.z;

        out[i].pos.x = (out[i].pos.x + 1) * m_width / 2;
        out[i].pos.y = (out[i].pos.y + 1) * m_height / 2;
        out[i].r = vertex[i].r * m_ambientR;
        out[i].g = vertex[i].g * m_ambientG;
        out[i].b = vertex[i].b * m_ambientB;
        out[i].tex = vertex[i].tex;
    }

    gtypes::Vector3 n, v;
    float cosFi;
    for( int i = 0; i < m_lights.size(); i++ )
    {
        for( int j = 0; j < vertexCount; j++ )
        {
            n = nmat * vertex[j].normal;
            v=(m_modelviewMatrix * vertex[j].pos);
            v = m_lights[i]->m_light_pos - v;
            v.normalise();
            cosFi = v.dotProduct(n);
            cosFi=(cosFi > 0) ? cosFi : 0;

            out[j].r += m_lights[i]->m_r * cosFi;
            if (out[j].r > 255) out[j].r=255;
            out[j].g += m_lights[i]->m_g * cosFi;
            if (out[j].g > 255) out[j].g=255;
            out[j].b += m_lights[i]->m_b * cosFi;
            if (out[j].b > 255) out[j].b=255;
        }
    }
}

void Renderer::setAmbientLight(float r, float g, float b)
{
    m_ambientR = r;
    m_ambientG = g;
    m_ambientB = b;
}

void Renderer::addLight(Light* light)
{
    m_lights.append(light);
}

void Renderer::removeLight(Light* light)
{
    for( int i = 0; i < m_lights.size(); i++ )
    {
        if( m_lights[i] == light )
        {
            m_lights.removeAt(i);
            i--;
        }
    }
}

void Renderer::clearLights()
{
    m_lights.clear();
}

void Renderer::setTarget(QImage* target)
{
    m_target=target;
    m_width=target->width();
    m_height=target->height();
    m_stride=target->bytesPerLine();
    m_data=target->bits();
}

void Renderer::rotate(float x, float y, float z, float angle)
{
    m_modelviewMatrix.rotate(x, y, z, angle);
}

void Renderer::translate(float x, float y, float z)
{
    m_modelviewMatrix.translate(x,y,z);
}

void Renderer::m_arrangeVerticesCCW(RendererVertex& v1, RendererVertex& v2, RendererVertex& v3)
{
    RendererVertex t;
    //RenderVertex v[3]={v1,v2,v3};
    if (v2.pos.y <= v1.pos.y) { t=v2; v2=v1; v1=t; }
    if (v3.pos.y <= v1.pos.y) { t=v3; v3=v1; v1=t; }
    if (v3.pos.x <= v2.pos.x) { t=v2; v2=v3; v3=t; }
}

void Renderer::m_arrangeVerticesY(RendererVertex& v1, RendererVertex& v2, RendererVertex& v3)
{
    RendererVertex t;
    //RenderVertex v[3]={v1,v2,v3};
    if (v2.pos.y <= v1.pos.y) { t=v2; v2=v1; v1=t; }
    if (v3.pos.y <= v1.pos.y) { t=v3; v3=v1; v1=t; }
    if (v3.pos.y <= v2.pos.y) { t=v2; v2=v3; v3=t; }
}






void Renderer::scale(float factor)
{
    m_modelviewMatrix.scale(factor);
}

void Renderer::identity()
{
    m_modelviewMatrix.setIdentity();
}



void Renderer::setProjectionMatrix( float fov, float aspect, float near, float far )
{
    m_projectionMatrix.perspective(fov,aspect,near,far);
}



void Renderer::lookAt(const gtypes::Vector3& eye, const gtypes::Vector3& target, const gtypes::Vector3& up)
{
   m_modelviewMatrix.lookAt( eye, target, up );
}

void Renderer::lookAt(const float* eye, const float* target, const float* up)
{
   m_modelviewMatrix.lookAt( eye, target, up );
}

void Renderer::drawTriangleSegment(float ystart,float yend,float yinc,Interpolant base, Interpolant left, Interpolant right)
{
    unsigned char* line;
    Interpolant l=base,r=base;
    int ix,iy,xend;

    if (ystart < 0) ystart=0; if (ystart > m_height-1) ystart=m_height-1;
    if (yend < 0) yend=0; if (yend > m_height-1) yend=m_height-1;
    Interpolant diff(0,0,0,0,0,0,0,0,0,0);
    for (iy=ystart;iy!=yend;iy+=yinc)
    {
        line=m_data+iy*m_stride;
        ix=floor(l.x); xend=ceil(r.x);
        if (ix < 0) ix=0; if (xend < 0) xend=0; if (xend > m_width) xend=m_width;
        diff=r;
        diff-=l;
        diff/=xend-ix;
        scan(line+ix*3,xend-ix,l,diff);
        l+=left; r+=right;
  //      base+=left;
    }
}



void Renderer::render(RendererVertex v[3])
{
    m_arrangeVerticesY(v[0],v[1],v[2]);
    float h=v[1].pos.y-v[0].pos.y;
    gtypes::Vector3 v20=v[2].pos-v[0].pos;
    v20.normalise();

    RendererVertex temp,v1=v[2]; v1.pos=v[0].pos+v20*(h/v20.dotProduct(gtypes::Vector3(0,1,0)));
    float slice=(v1.pos-v[0].pos).length()/(v[2].pos-v[0].pos).length();
    v1.pos.z=v[0].pos.z+(v[2].pos.z-v[0].pos.z)*slice;
    v1.tex.x=v[0].tex.x+(v[2].tex.x-v[0].tex.x)*slice;
    v1.tex.y=v[0].tex.y+(v[2].tex.y-v[0].tex.y)*slice;
    v1.r=v[0].r+(v[2].r-v[0].r)*slice;
    v1.g=v[0].g+(v[2].g-v[0].g)*slice;
    v1.b=v[0].b+(v[2].b-v[0].b)*slice;



    RendererVertex v2=v[1];
    if (v2.pos.x < v1.pos.x) { temp=v1; v1=v2; v2=temp; }

    float l;
    int ystart=v[0].pos.y,yend= v[1].pos.y;//+(v[1].pos.y-(int) v[1].pos.y > 0.5f);
    if (v[1].pos.y-v[0].pos.y > 0.5f)
    {
        l=yend-ystart;
        drawTriangleSegment(ystart,yend, 1,
            Interpolant(v[0].pos.x,v[0].pos.z,1/v[0].pos.z,v[0].r,v[0].g,v[0].b,
                        v[0].tex.x,v[0].tex.y,v[0].tex.x/v[0].pos.z,v[0].tex.y/v[0].pos.z),
            Interpolant((v1.pos.x-v[0].pos.x)/l,(v1.pos.z-v[0].pos.z)/l,(1/v1.pos.z-1/v[0].pos.z)/l,
                        (v1.r-v[0].r)/l,(v1.g-v[0].g)/l,(v1.b-v[0].b)/l,
                        (v1.tex.x-v[0].tex.x)/l,(v1.tex.y-v[0].tex.y)/l,
                        (v1.tex.x/v1.pos.z-v[0].tex.x/v[0].pos.z)/l,(v1.tex.y/v1.pos.z-v[0].tex.y/v[0].pos.z)/l),
            Interpolant((v2.pos.x-v[0].pos.x)/l,(v2.pos.z-v[0].pos.z)/l,(1/v2.pos.z-1/v[0].pos.z)/l,
                        (v2.r-v[0].r)/l,(v2.g-v[0].g)/l,(v2.b-v[0].b)/l,
                        (v2.tex.x-v[0].tex.x)/l,(v2.tex.y-v[0].tex.y)/l,
                        (v2.tex.x/v2.pos.z-v[0].tex.x/v[0].pos.z)/l,(v2.tex.y/v2.pos.z-v[0].tex.y/v[0].pos.z)/l));
    }
    if (v[2].pos.y - v[1].pos.y >= 0.5f)
    {
        ystart=v[2].pos.y;
        yend--;
        l=yend-ystart;
        drawTriangleSegment(ystart,yend,-1,
            Interpolant(v[2].pos.x,v[2].pos.z,1/v[2].pos.z,v[2].r,v[2].g,v[2].b,
                        v[2].tex.x,v[2].tex.y,v[2].tex.x/v[2].pos.z,v[2].tex.y/v[2].pos.z),
            Interpolant((v[2].pos.x-v1.pos.x)/l,(v[2].pos.z-v1.pos.z)/l,(1/v[2].pos.z-1/v1.pos.z)/l,
                        (v[2].r-v1.r)/l,(v[2].g-v1.g)/l,(v[2].b-v1.b)/l,
                        (v[2].tex.x-v1.tex.x)/l,(v[2].tex.y-v1.tex.y)/l,
                        (v[2].tex.x/v[2].pos.z-v1.tex.x/v1.pos.z)/l,(v[2].tex.y/v[2].pos.z-v1.tex.y/v1.pos.z)/l),
            Interpolant((v[2].pos.x-v2.pos.x)/l,(v[2].pos.z-v2.pos.z)/l,(1/v[2].pos.z-1/v2.pos.z)/l,
                        (v[2].r-v2.r)/l,(v[2].g-v2.g)/l,(v[2].b-v2.b)/l,
                        (v[2].tex.x-v2.tex.x)/l,(v[2].tex.y-v2.tex.y)/l,
                        (v[2].tex.x/v[2].pos.z-v2.tex.x/v2.pos.z)/l,(v[2].tex.y/v[2].pos.z-v2.tex.y/v2.pos.z)/l));

    }

}
